package com.xuelang.pipeline.artifact.client.common.util;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.xuelang.pipeline.artifact.client.common.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.Map;

/**
 * web jwt工具类
 *
 * @author xiwen.zxg
 * @since 2024/5/17
 */
@Slf4j
public class WebJwtUtil {

    public static void main(String[] args) {
        String zh = createTokenWithChnAndTag("13122222222", "zh");
        System.out.println(zh);
        System.out.println(JwtUtil.verifyGetPhoneForMdc(zh));
        System.out.println(getPhone(zh));
    }

    /**
     * jwt 密钥
     */
    private static String jwtSecret = "123456";

    /**
     * 根据号码生成token
     *
     * @param phone    手机号
     * @param loginChn 登录页面渠道号
     * @return token
     */
    public static String createTokenWithChnAndTag(String phone, String loginChn) {
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, JwtUtil.KEY);
        String encryptHex = aes.encryptHex(phone);
        Date date = DateTime.now();
        return JWT.create()
                // 手机号码
                .withClaim(JwtUtil.THREAD_PHONE, encryptHex)
                .withClaim(JwtUtil.THREAD_LOGIN_CHN, loginChn)
                // 签名是有谁生成 例如 服务器
                .withIssuer(JwtUtil.ISSUER)
                // 签名的主题
                .withSubject(encryptHex)
                // 该jwt都是不可用的时间
                //.withNotBefore(new Date())
                // 签名的观众 也可以理解谁接受签名的
                .withAudience("DevOps")
                // 生成签名的时间
                .withIssuedAt(date)
                // 签名过期的时间
                .withExpiresAt(DateUtil.offsetWeek(date, JwtUtil.EXPIRES_WEEK))
                /*签名 Signature */
                .sign(Algorithm.HMAC256(jwtSecret));
    }

    /**
     * 验证token
     *
     * @param token 验证token
     */
    public static void verifyToken(String token) {
        if (StringUtils.isBlank(token)) {
            throw new BizException("token错误");
        }
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret))
                .withIssuer(JwtUtil.ISSUER).build();
        try {
            jwtVerifier.verify(token);
        } catch (TokenExpiredException e) {
            throw new BizException("token已过期");
        } catch (JWTVerificationException e) {
            throw new BizException("token校验失败");
        }
    }

    /**
     * 获取token手机号
     *
     * @param token token
     * @return 手机号
     */
    public static String getPhone(String token) {
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret))
                .withIssuer(JwtUtil.ISSUER).build();
        DecodedJWT jwt;
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, JwtUtil.KEY);

        try {
            if (StringUtils.isBlank(token)) {
                throw new BizException("请输入token");
            }
            jwt = jwtVerifier.verify(token);

            Map<String, Claim> claims = jwt.getClaims();
            Claim claim1 = claims.get(JwtUtil.THREAD_PHONE);
            if (ObjectUtil.isNotNull(claim1)) {
                String encryptHex = claim1.asString();
                return aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);
            }
            throw new BizException("获取用户信息异常");
        } catch (TokenExpiredException e) {
            throw new BizException(e);
        } catch (JWTVerificationException e) {
            throw new BizException(e);
        }
    }

    /**
     * 获取token 验证后的 DecodedJWT
     *
     * @param token token
     * @return DecodedJWT
     */
    public static DecodedJWT getDecodedJwt(String token) {
        if (StringUtils.isBlank(token)) {
            throw new BizException("请输入token");
        }
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret))
                .withIssuer(JwtUtil.ISSUER).build();
        try {
            return jwtVerifier.verify(token);
        } catch (TokenExpiredException e) {
            throw new BizException("用户认证超时");
        } catch (JWTVerificationException e) {
            throw new BizException("用户验证失败");
        }
    }

    /**
     * 获取token手机号
     *
     * @param jwt DecodedJWT
     * @return 手机号
     */
    public static String getPhone(DecodedJWT jwt) {
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, JwtUtil.KEY);
        Map<String, Claim> claims = jwt.getClaims();
        Claim claim1 = claims.get(JwtUtil.THREAD_PHONE);
        if (ObjectUtil.isNotNull(claim1)) {
            String encryptHex = claim1.asString();
            return aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);
        }
        throw new BizException("获取用户信息异常");
    }

    /**
     * 获取登录渠道号
     *
     * @param jwt DecodedJWT
     * @return 手机号
     */
    public static String getLoginChn(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        Claim claim1 = claims.get(JwtUtil.THREAD_LOGIN_CHN);
        if (ObjectUtil.isNotNull(claim1)) {
            return claim1.asString();
        }
        return "";
    }

    /**
     * 获取token过期时长设置
     *
     * @param token 鉴权token
     * @return 时长秒数
     */
    public static long getExpireSeconds(String token) {
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret))
                .withIssuer(JwtUtil.ISSUER).build();
        DecodedJWT jwt;
        try {
            if (StringUtils.isBlank(token)) {
                return 0;
            }
            jwt = jwtVerifier.verify(token);
            Map<String, Claim> claims = jwt.getClaims();
            Claim claim1 = claims.get("exp");
            long exp = claim1.asLong();
            Date now = DateTime.now();
            return DateUtil.between(now, DateUtil.date(exp * 1000), DateUnit.SECOND);
        } catch (JWTVerificationException e) {
            return 0;
        }
    }
}
